package p.ithorns.support.license.service;

import lombok.extern.slf4j.Slf4j;
import p.ithorns.framework.common.codec.AESCodec;
import p.ithorns.framework.common.codec.MD5Codec;
import p.ithorns.framework.common.codec.RSACodec;
import p.ithorns.framework.common.model.Pair;
import p.ithorns.framework.common.utils.StringUtil;
import p.ithorns.framework.common.utils.*;
import p.ithorns.framework.common.uuid.UuidUtil;
import p.ithorns.support.license.model.Authorize;
import p.ithorns.support.license.model.License;
import p.ithorns.support.license.model.LicenseBody;
import p.ithorns.support.license.model.entity.LicenseEntity;

import javax.crypto.SecretKey;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;

/**
 * Grantter
 *
 * @author Ht.L
 * @date 2023-12-12 13:32
 * @since 1.0.0
 */
@Slf4j
public class Grantor {

    public static License genLicense(String sn) {
        License lic = new License();
        lic.setAuthCode(sn);
        lic.setGrantTime(LocalDateTime.now());
        lic.setParam(null);
        return lic;
    }


    public static License genLicense(LicenseEntity entity) {
        License lic = new License();
        lic.setAuthCode(entity.getAuthCode());
        lic.setExpiration(entity.getExpiration());
        lic.setGrantTime(entity.getCreateTime());
        lic.setParam(null);
        return lic;
    }

    /**
     * AES加密License
     *
     * @param lic -License
     * @return Cipher - 密文
     * @throws Exception -
     */
    public static String encrypt(String authCode, String lic) throws Exception {
        if (StringUtil.isBlank(lic)) {
            return "";
        }

        // 使用唯一码做盐对证书进行加密
        SecretKey secretKey = AESCodec.generateKey(authCode);
        return AESCodec.encrypt(lic, secretKey);
    }

    public static String genAuthCode(String sn, String authCode)
            throws UnsupportedEncodingException, NoSuchAlgorithmException {
        return MD5Codec.md5(sn + "&" + authCode, sn);
    }

    public static void main(String[] args) throws Exception {
        // 客户端向服务端发送机器码并获取公钥
        log.info("【客户端】-> 向服务端发送机器码并请求公钥");

        // 服务端生成RSA密钥对
        Pair<String, String> keyPair = RSACodec.getKeyPair();
        String privateKey = keyPair.getLeft();
        String publicKey = keyPair.getRight();
        log.info("【服务端】-> 生成RSA密钥对");

        // 服务端产生一个随机授权码
        String authCode = UuidUtil.uuid(privateKey);
        Authorize auth = new Authorize(publicKey, authCode);
        log.info("【服务端】-> 响应公钥&授权码: {}", auth);

        // 客户端生成唯一码， 用公钥加密后发送给服务端
        String sn = DeviceUtil.getSn();
        log.info("【客户端】-> 获取机器码: {}", sn);

        // 这里是关键，客户端通过随机码生成一个种子，通过RSA加密后传递给服务端。
        // 相当于把把钥匙放进保险箱，然后快递给服务端。而本地使用同样算法可以生成钥匙的副本
        // 服务端和客户端都通过这个种子生成AES-Key来对数据进行加解密，这样一来，就不用直接传递AES-Key了
        String feed = genAuthCode(sn, auth.getAuthCode());
        log.info("【客户端】-> 生成AES种子: {}", feed);

        String rsaSnCodeEncrypt = RSACodec.encryptPublic(feed, publicKey);
        log.info("【客户端】-> 加密SN: {}", rsaSnCodeEncrypt);
        log.info("【客户端】-> 向服务端发送加密SN");

        // 服务端收到SN, 解密后生成License
        String rsaSnCodeDecrypt = RSACodec.decrypt(rsaSnCodeEncrypt, privateKey);
        log.info("【服务端】-> 解密SN: {}", rsaSnCodeDecrypt);

        Grantor grantor = new Grantor();
        License license = grantor.genLicense(rsaSnCodeDecrypt);
        log.info("【服务端】-> 生成Lic: {}", license);

        // 服务端对License进行加密
        SecretKey secretKey = AESCodec.generateKey(rsaSnCodeDecrypt);
        // 使用唯一码做盐对证书进行加密
        String encrypt = AESCodec.encrypt(JsonUtil.toJson(license), secretKey);
        log.info("【服务端】-> AES加密Lic: {}", encrypt);

        // 服务端对AES加密内容进行签名
        String rsaSign = RSACodec.sign(encrypt, privateKey);
        log.info("【服务端】-> RSA私钥签名: {}", rsaSign);
        LicenseBody lb = new LicenseBody(encrypt, rsaSign);
        log.info("【服务端】-> 响应签名内容给客户端: {}", lb);

        // 客户端进行校验
        boolean verify = RSACodec.verify(encrypt, publicKey, rsaSign);
        log.info("【客户端】-> RSA公钥验证: {}", verify);

        // 客户端通过同样算法生成AES-Key
        SecretKey aesKey = AESCodec.generateKey(feed);
        log.info("【客户端】-> 生成AES-Key：{}", RSACodec.encodeKey(aesKey));
        String aes = AESCodec.decrypt(encrypt, aesKey);
        log.info("【客户端】-> AES解密：{}", aes);

        // 对AES-Key私钥加密-公钥解密. 不可取：publicKey是公开的，任何获取到publicKey的客户端都可以解密AES-Key
//        String rsaPriAesKey = RSACodec.encryptPrivate(RSACodec.encodeKey(aesKey), privateKey);
//        log.info("【服务端】-> 加密AES-KEY: {}", rsaPriAesKey);
//        String rsaPubAesKey = RSACodec.decryptPublic(rsaPriAesKey, publicKey);
//        log.info("【客户端】-> 解密AES-KEY: {}", rsaPubAesKey);
    }

}